﻿//------------------------------------------------------------------------------------------
// File: <FTReactorUnit.cs>
// Purpose: implement <FT reactor unit complying with CAPE-OPEN.>
//
// @author <Yun Hua>
// @version 1.0 2010/04/12
// 
// @Updates <2010-5-13, Yun Hua, Add routine to establish Xco with the catalyst activity, residence time.>
// Copyright (C) 2010, YUN HUA.
//-----------------------------------------------------------------------------------------//

using System;
using System.Collections.Generic;
using System.Text;
using Mose.CapeOpenToolkit.UnitProxy;
using System.Runtime.InteropServices;
using CAPEOPEN100;

namespace Yashen.SimLab
{
    internal enum CalcMode
    {
        Simple,
        ModeI,
        ModeII
    }

    internal enum ConversionBase
    {
        H2,
        CO
    }

    /// <summary>
    /// Data structure recording the temperary data for the iterations.
    /// </summary>
    internal struct IntermCalcData
    {
        private static string delimiter = "\t"; 
        public DateTime dataTime;
        public double Xbase;    // Main base component conversion, such as Xco or Xh2.
        public double Xpair;    // The other component conversion, pair of Xbase.
        public double usageRatio; // UR = Total H2 changed inside reactor / Total CO changed inside the reactor. 
        public double flowIn;
        public double temperatureIn;
        public double pressureIn;
        public double enthalpyIn;
        public double flowOut;
        public double temperatureOut;
        public double pressureOut;
        public double enthalpyOut;
        public double ratioH2COIn;
        public double ratioH2COOut;

        public string BuildString()
        {
            StringBuilder str = new StringBuilder();
            str.Append(dataTime.ToLongTimeString());
            str.Append(delimiter);
            str.Append(Xbase);
            str.Append(delimiter);
            str.Append(Xpair);
            str.Append(delimiter);
            str.Append(usageRatio);
            str.Append(delimiter);
            str.Append(flowIn);
            str.Append(delimiter);
            str.Append(temperatureIn);
            str.Append(delimiter);
            str.Append(pressureIn);
            str.Append(delimiter);
            str.Append(enthalpyIn);
            str.Append(delimiter);
            str.Append(ratioH2COIn);
            str.Append(delimiter);
            str.Append(flowOut);
            str.Append(delimiter);
            str.Append(temperatureOut);
            str.Append(delimiter);
            str.Append(pressureOut);
            str.Append(delimiter);
            str.Append(enthalpyOut);
            str.Append(delimiter);
            str.Append(ratioH2COOut);
            str.Append(delimiter);
            return str.ToString();
        }
    }

    public partial class FTReactorUnit : Mose.CapeOpenToolkit.UnitProxy.CapeOpenUnitSimple
    {
        #region Private Members

        private double[] simpleModeCoeffs;
        private double[] fracMap;
        private double[] inactiveCompsMap;
        private double pressureDrop = 0.0;      // In pa
        private double fixedTemperature = 273.15;  // In K

        private double inputTotalFlow;          // mol/s
        private double inputPressure;           // pa 

        private readonly int COMP_OFFSET = 3;   // Jump three components: H2, CO, H2O
        private CalcMode calcMode = CalcMode.Simple;
        private ConversionBase convBase = ConversionBase.CO;
        private double conversion;              // Base component conversion, based on "convBase", it will be refreshed on every iteration calculation.
        private double conversionPair;          // The pair component conversion, the other than "convBase", it will be refreshed on every iteration calculation.
        private double usageRatio;              // UR = Total H2 changed inside reactor / Total CO changed inside the reactor, updating on every iteration calculation.

        private List<IntermCalcData> calcSequence = new List<IntermCalcData>();

        #endregion

        #region Private Members Calculation Mode I

        private double kCoeff;
        private double reactorVol;  // in L
        private double selAlpha;
        private double[] selsC1toCnAdjusted; // Adjusted C1 to Cn selectivities

        #endregion

        #region Private Members Calculation Mode II

        private double k1Coeff;
        private double k2Coeff;

        #endregion

        #region Properties

        /// <summary>
        /// Get or set the pressure drop
        /// </summary>       
        public double PressureDrop
        {
            get { return pressureDrop; }
            set { pressureDrop = value; }
        }

        /// <summary>
        /// Get or set the fixed temperature for the isothermal condition.
        /// </summary>       
        public double FixedTemperature
        {
            get { return fixedTemperature; }
            set { fixedTemperature = value; }
        }
   
        /// <summary>
        /// Get or set the conefficients
        /// </summary>
        public double[] Coeffs
        {
            get { return simpleModeCoeffs; }
            set { simpleModeCoeffs = value; }
        }

        /// <summary>
        /// Get or set the component mapping for the flow fractions.
        /// </summary>       
        public double[] FracMap
        {
            get { return fracMap; }
            set { fracMap = value; }
        }

        /// <summary>
        /// Get or set the inactive component map.
        /// </summary>
        public double[] InactiveCompsMap
        {
            get { return inactiveCompsMap; }
            set { inactiveCompsMap = value; }
        }

        /// <summary>
        /// Get or set the calculation mode.
        /// </summary>      
        internal CalcMode CalcMode
        {
            get { return calcMode; }
            set { calcMode = value; }
        }

        /// <summary>
        /// Get or set the calculation sequence.
        /// </summary>
        internal List<IntermCalcData> CalcSequence
        {
            get { return calcSequence; }
            set { calcSequence = value; }
        }

        /// <summary>
        /// Get or set the K coefficients for Xco calculation.
        /// </summary>       
        public double KCoeff
        {
            get { return kCoeff; }
            set { kCoeff = value; }
        }

        /// <summary>
        /// Get or set the k1 coefficient for calculating CH4 yield
        /// </summary>
        public double K1Coeff
        {
            get { return k1Coeff; }
            set { k1Coeff = value; }
        }

        /// <summary>
        /// Get or set the k2 coefficient for calculating C2+ yield
        /// </summary>
        public double K2Coeff
        {
            get { return k2Coeff; }
            set { k2Coeff = value; }
        }

        /// <summary>
        /// Get or set the reactor volume.
        /// </summary>
        public double ReactorVol
        {
            get { return reactorVol; }
            set { reactorVol = value; }
        }

        /// <summary>
        /// Get or set the Space Time Yields table.
        /// </summary>      
        public List<KeyValuePair<double, double>> SpaceTimeYields
        {
            get { return FTPropPackage.SpaceTimeYields; }
            set { FTPropPackage.SpaceTimeYields = value; }
        }

        /// <summary>
        /// Get or set the selectivity alpha value.
        /// </summary>
        public double SelAlpha
        {
            get { return selAlpha; }
            set { selAlpha = value; }
        }

        /// <summary>
        /// Get or set the adjusted C1-Cn selectivties.
        /// </summary>
        public double[] SelsC1toCnAdjusted
        {
            get { return selsC1toCnAdjusted; }
            set { selsC1toCnAdjusted = value; }
        }

        #endregion

        #region Override Methods

        protected override void OnCalculate()
        {
            inputTotalFlow = InPort.TotalFlow;
            inputPressure = InPort.Pressure;

            // The check step is not neccessary.
            ICapeThermoMaterialObject thermolObj = InPort.ConnectedObject as ICapeThermoMaterialObject;

            if (thermolObj == null)
            {
                throw new InvalidCastException("Invalid material object cast");
            }

            if (fracMap == null || fracMap.Length != FTPropPackage.FT_COMPONENT_COUNT)
            {
                throw new InvalidOperationException("Component fraction array is not set");
            }

            #region Comment
            // Pay attention!!!
            // In Pro/II, reset the input data before running a complete new simulation.
            // Otherwise the cached data will give impact on the new simulation.
            // To reset the cache data, select menu: Input->reset input data.

            // There is side impact calling InPort.CalculateProperty(Enthalpy), it will change the previous
            // enthalpy of the input port. I not sure why it is appears.
            // InPort.CalculateProperty(
            //   CapeOpenThermoMaterialPropertyType.Enthalpy, CapeOpenPhaseType.Overall, CapeOpenCalculationType.Mixture);
            #endregion

            switch (calcMode)
            {
                case CalcMode.Simple:
                    CalcSimpleMode();
                    break;
                case CalcMode.ModeI:
                    CalcModeI();
                    break;
                case CalcMode.ModeII:
                    CalcModeII();
                    break;
                default:
                    break;
            }

            IntermCalcData calcData = new IntermCalcData();
            calcData.dataTime = DateTime.Now;
            calcData.flowIn = InPort.TotalFlow;
            calcData.pressureIn = InPort.Pressure;
            calcData.temperatureIn = InPort.Temperature;
            calcData.enthalpyIn = InPort.Enthalpy;
            calcData.flowOut = OutPort.TotalFlow;
            calcData.pressureOut = OutPort.Pressure;
            calcData.temperatureOut = OutPort.Temperature;
            calcData.enthalpyOut = OutPort.Enthalpy;
            calcData.Xbase = conversion;
            calcData.Xpair = conversionPair;
            calcData.usageRatio = usageRatio;
            calcData.ratioH2COIn = InPort.Fraction[Convert.ToInt32(fracMap[FTPropPackage.INDEX_H2])] / 
                InPort.Fraction[Convert.ToInt32(fracMap[FTPropPackage.INDEX_CO])];
            calcData.ratioH2COOut = OutPort.Fraction[Convert.ToInt32(fracMap[FTPropPackage.INDEX_H2])] / 
                OutPort.Fraction[Convert.ToInt32(fracMap[FTPropPackage.INDEX_CO])];
            calcSequence.Add(calcData);
        }

        protected override void OnEdit()
        {
            ParameterForm.Edit(this);
        }

        #endregion

        #region Private Methods

        /// <summary>
        /// Calculate the properties after a reaction. 
        /// They include the total flow, fraction and the enthalpy.
        /// </summary>
        /// <param name="baseComp">The conversion base component.</param>
        /// <param name="fracIn">The input port component fractions.</param>
        /// <param name="convCoeffs">The conversion coefficients for how many base component has been
        /// conerted to the target componet.</param>
        /// <param name="fracOut">The output component fractions.</param>
        /// <param name="totalFlowOut">The output total flow rate in ml/s.</param>
        /// <param name="enthalpyOut">The calculated enthalpy.</param>
        private void CalcPropOut(ConversionBase baseComp, double[] fracIn, double[] convCoeffs, out double[] fracOut, 
            out double totalFlowOut, out double enthalpyOut)
        {
            if (fracIn.Length != InPort.ComponentsCount ||
                (FTPropPackage.FT_COMPONENT_COUNT + InactiveCompsMap.Length != InPort.ComponentsCount)
                )
            {
                throw new ArgumentException("The component count is not matched.", "fracIn");
            }

            int compCount = InPort.ComponentsCount;
            double totalFlowIn = InPort.TotalFlow;
            double[] moleArray = new double[compCount];

            fracOut = new double[compCount];
    
            // Active components
            for (int i = 0; i < FTPropPackage.FT_COMPONENT_COUNT; ++i)  
            {
                moleArray[i] = totalFlowIn * fracIn[Convert.ToInt32(fracMap[i])];
            }
            // Inactive components
            for (int i = 0; i < inactiveCompsMap.Length; ++i)
            {
                moleArray[i + FTPropPackage.FT_COMPONENT_COUNT] = 
                    totalFlowIn * fracIn[Convert.ToInt32(inactiveCompsMap[i])];
            }

            // FT reaction using the conversion coefficients
            double convCOMole = 0.0;
            double convH2Mole = 0.0;
            double convH2OMole = 0.0;
            double converted = 0.0;
            double baseComponent = 0.0;
            enthalpyOut = 0.0;

            // Mass and energy calculation: CH4, C(n+1)H(2n+2) and CO2
            if (baseComp == ConversionBase.CO)
            {
                baseComponent = moleArray[FTPropPackage.INDEX_CO];
                for (int i = 0; i < convCoeffs.Length; i++)
                {
                    // Calculate Cn+ mole
                    converted =
                        FTPropPackage.StoiCoefCO[i] > 0 ? (baseComponent * convCoeffs[i]) : (-baseComponent * convCoeffs[i]);
                    moleArray[i + COMP_OFFSET] += converted * (FTPropPackage.StoiCoefCn[i] / FTPropPackage.StoiCoefCO[i]);
                    convCOMole += converted;
                    convH2Mole += converted * (FTPropPackage.StoiCoefH2[i] / FTPropPackage.StoiCoefCO[i]);
                    convH2OMole += -converted;
                    enthalpyOut += Math.Abs(converted) *
                        (FTPropPackage.StdHeatFormIn[i + COMP_OFFSET] * Math.Abs(FTPropPackage.StoiCoefCn[i] / FTPropPackage.StoiCoefCO[i]) +
                        FTPropPackage.StdHeatFormIn[FTPropPackage.INDEX_H2O] * Math.Abs(FTPropPackage.StoiCoefH2O[i] / FTPropPackage.StoiCoefCO[i]) -
                        FTPropPackage.StdHeatFormIn[FTPropPackage.INDEX_H2] * Math.Abs(FTPropPackage.StoiCoefH2[i] / FTPropPackage.StoiCoefCO[i]) -
                        FTPropPackage.StdHeatFormIn[FTPropPackage.INDEX_CO]
                        );
                }
                conversionPair = Math.Abs(convH2Mole / moleArray[FTPropPackage.INDEX_H2]);
            }
            else if (baseComp == ConversionBase.H2)
            {
                baseComponent = moleArray[FTPropPackage.INDEX_H2];
                for (int i = 0; i < convCoeffs.Length; i++)
                {
                    // Calculate Cn+ mole
                    converted =
                        FTPropPackage.StoiCoefH2[i] > 0 ? (baseComponent * convCoeffs[i]) : (-baseComponent * convCoeffs[i]);
                    moleArray[i + COMP_OFFSET] += converted * (FTPropPackage.StoiCoefCn[i] / FTPropPackage.StoiCoefH2[i]);
                    convH2Mole += converted;
                    convCOMole += converted * (FTPropPackage.StoiCoefCO[i] / FTPropPackage.StoiCoefH2[i]);
                    convH2OMole += converted * (FTPropPackage.StoiCoefH2O[i] / FTPropPackage.StoiCoefH2[i]);
                    enthalpyOut += Math.Abs(converted) *
                        (FTPropPackage.StdHeatFormIn[i + COMP_OFFSET] * Math.Abs(FTPropPackage.StoiCoefCn[i] / FTPropPackage.StoiCoefH2[i]) +
                        FTPropPackage.StdHeatFormIn[FTPropPackage.INDEX_H2O] * Math.Abs(FTPropPackage.StoiCoefH2O[i] / FTPropPackage.StoiCoefH2[i]) -
                        FTPropPackage.StdHeatFormIn[FTPropPackage.INDEX_CO] * Math.Abs(FTPropPackage.StoiCoefCO[i] / FTPropPackage.StoiCoefH2[i]) -
                        FTPropPackage.StdHeatFormIn[FTPropPackage.INDEX_H2]
                        );
                }
                conversionPair = Math.Abs(convCOMole / moleArray[FTPropPackage.INDEX_CO]);
            }
            else
            {
                throw new InvalidOperationException("Unsupported base conversion component.");
            }

            moleArray[FTPropPackage.INDEX_H2] += convH2Mole;
            moleArray[FTPropPackage.INDEX_CO] += convCOMole;
            moleArray[FTPropPackage.INDEX_H2O] += convH2OMole;

            usageRatio = Math.Abs(convH2Mole) / Math.Abs(convCOMole);

            totalFlowOut = 0.0;
            for (int i = 0; i < moleArray.Length; ++i)
            { 
                totalFlowOut += moleArray[i];
            }

            for (int i = 0; i < FTPropPackage.FT_COMPONENT_COUNT; ++i)
            {
                fracOut[Convert.ToInt32(fracMap[i])] = moleArray[i] / totalFlowOut;
            }
            for (int i = 0; i < inactiveCompsMap.Length; ++i)
            {
                fracOut[Convert.ToInt32(inactiveCompsMap[i])] = 
                    moleArray[i + FTPropPackage.FT_COMPONENT_COUNT] / totalFlowOut;
            }
        }

        /// <summary>
        /// Calculation in the simple mode.
        /// </summary>
        private void CalcSimpleMode()
        {
            if (simpleModeCoeffs == null || simpleModeCoeffs.Length != FTPropPackage.FT_COMPONENT_COUNT - COMP_OFFSET)
            {
                throw new InvalidOperationException("Coefficients is not set.");
            }

            double enthalpyIn = InPort.Enthalpy;
            double[] fractionOut = null;
            double totalFlowOut = 0.0;
            double enthalpyOut = 0.0;

            conversion = 0.0;
            foreach (double d in simpleModeCoeffs)
            {
                conversion += d;
            }
            CalcPropOut(convBase, InPort.Fraction, simpleModeCoeffs, out fractionOut, out totalFlowOut, out enthalpyOut);
            OutPort.Pressure = InPort.Pressure - pressureDrop;
            OutPort.Temperature = fixedTemperature;
            OutPort.TotalFlow = totalFlowOut;
            OutPort.Fraction = fractionOut;

            enthalpyOut = enthalpyIn; // for isothermal only
            double DH = enthalpyOut - enthalpyIn;
            OutPort.CalculateEquilibrium(CapeOpenFlashType.TP);
            OutPort.Enthalpy += DH;
            OutPort.CalculateProperty(CapeOpenThermoMaterialPropertyType.Enthalpy,
                CapeOpenPhaseType.Overall, CapeOpenCalculationType.Mixture);
        }

        /// <summary>
        /// Calculation using the Xco = k * STY(T) * (1 / GHSV)
        /// </summary>
        private void CalcModeI()
        {
            if (FTPropPackage.SpaceTimeYields == null)
            {
                throw new InvalidOperationException("The space time yield table is null.");
            }

            double enthalpyIn = InPort.Enthalpy;
            double[] fractionOut = null;
            double totalFlowOut = 0.0;
            double enthalpyOut = 0.0;

            double[] convCoeffs = MoleFracsUsingChainGrowth(selAlpha);
            //double ratio = InPort.Fraction[Convert.ToInt32(fracMap[FTPropPackage.INDEX_H2])] /
            //    InPort.Fraction[Convert.ToInt32(fracMap[FTPropPackage.INDEX_CO])];
            //ConversionBase convBase = ratio > 2.06 ? ConversionBase.CO : ConversionBase.H2;

            CalcPropOut(convBase, InPort.Fraction, convCoeffs, out fractionOut, out totalFlowOut, out enthalpyOut);
            OutPort.Pressure = InPort.Pressure - pressureDrop;
            OutPort.Temperature = fixedTemperature;
            OutPort.TotalFlow = totalFlowOut;
            OutPort.Fraction = fractionOut;

            enthalpyOut = enthalpyIn; // for isothermal only
            double DH = enthalpyOut - enthalpyIn;

            OutPort.CalculateEquilibrium(CapeOpenFlashType.TP);
            OutPort.Enthalpy += DH;
            OutPort.CalculateProperty(CapeOpenThermoMaterialPropertyType.Enthalpy,
                CapeOpenPhaseType.Overall, CapeOpenCalculationType.Mixture);
        }

        /// <summary>
        /// Calculation considering the effects of H2/CO ratio.
        /// </summary>
        private void CalcModeII()
        {
            if (FTPropPackage.SpaceTimeYields == null)
            {
                throw new InvalidOperationException("The space time yield table is null.");
            }

            double enthalpyIn = InPort.Enthalpy;
            double[] fractionOut = null;
            double totalFlowOut = 0.0;
            double enthalpyOut = 0.0;

            double ratioH2CO = InPort.Fraction[Convert.ToInt32(fracMap[FTPropPackage.INDEX_H2])] /
                InPort.Fraction[Convert.ToInt32(fracMap[FTPropPackage.INDEX_CO])];
            double[] convCoeffs = MoleFracsUsingH2CORatio(ratioH2CO);
            //double ratio = InPort.Fraction[Convert.ToInt32(fracMap[FTPropPackage.INDEX_H2])] /
            //    InPort.Fraction[Convert.ToInt32(fracMap[FTPropPackage.INDEX_CO])];
            //ConversionBase convBase = ratio > 2.06 ? ConversionBase.CO : ConversionBase.H2;
            CalcPropOut(convBase, InPort.Fraction, convCoeffs, out fractionOut, out totalFlowOut, out enthalpyOut);
            OutPort.Pressure = InPort.Pressure - pressureDrop;
            OutPort.Temperature = fixedTemperature;
            OutPort.TotalFlow = totalFlowOut;
            OutPort.Fraction = fractionOut;

            enthalpyOut = enthalpyIn; // for isothermal only
            double DH = enthalpyOut - enthalpyIn;

            OutPort.CalculateEquilibrium(CapeOpenFlashType.TP);
            OutPort.Enthalpy += DH;
            OutPort.CalculateProperty(CapeOpenThermoMaterialPropertyType.Enthalpy,
                CapeOpenPhaseType.Overall, CapeOpenCalculationType.Mixture);
        }   

        /// <summary>
        /// Calculate the base component converting to C1-Cn's and CO2's mole fractions using the chain growth formula.
        /// The CH4 - Cx+ yields are revised according the laboratory results.
        /// </summary>
        /// <param name="alpha">The alpha value.</param>
        /// <returns>The base component converting to C1-Cn's mole fractions. Currently we use CO.</returns>
        private double[] MoleFracsUsingChainGrowth(double alpha)
        {
            if (alpha <= 0 || alpha >= 1)
            {
                throw new ArgumentOutOfRangeException("alpha");
            }

            //double ratio = InPort.Fraction[Convert.ToInt32(fracMap[FTPropPackage.INDEX_H2])] /
            //    InPort.Fraction[Convert.ToInt32(fracMap[FTPropPackage.INDEX_CO])];
            //ConversionBase convBase = ratio > 2.06 ? ConversionBase.CO : ConversionBase.H2;
            double[] convCoeffMap2BaseComp = CalcChainGrowth(convBase, selAlpha);

            // Adjustment with the experiment data, normalization firstly.
            if (selsC1toCnAdjusted == null)
            {
                throw new InvalidOperationException("Adjusted C1 - Cn selectivies array is null");
            }

            double normalizationFactor = 1;
            double leftTotal = 1;
            for (int i = 0; i < selsC1toCnAdjusted.Length; ++i)
            {
                normalizationFactor -= selsC1toCnAdjusted[i];
                leftTotal -= convCoeffMap2BaseComp[i];
                convCoeffMap2BaseComp[i] = selsC1toCnAdjusted[i];
            }
            for (int i = selsC1toCnAdjusted.Length; i < convCoeffMap2BaseComp.Length; ++i)
            {
                convCoeffMap2BaseComp[i] *= normalizationFactor/leftTotal;
            }

            // Calculate the CO conversion
            double sty = FTPropPackage.GetSTYByTemperature(fixedTemperature - 273.15, FTPropPackage.SpaceTimeYields);
            conversion = FTPropPackage.CalcXcoUsingSTY(kCoeff, sty, reactorVol, InPort.TotalFlow, 
                fixedTemperature, InPort.Pressure);

            if (conversion > 0.7)
            {
                conversion = 0.7;
            }

            // Final components' yield
            for (int i = 0; i < convCoeffMap2BaseComp.Length; ++i)
            {
                convCoeffMap2BaseComp[i] *= conversion;
            }

            return convCoeffMap2BaseComp;
        }

        /// <summary>
        /// Calculate the base component converting to C1-Cn's and CO2's mole fractions using the chain growth formula.
        /// The effects of H2/CO ratio is also considered. It influences the CH4 and the all C2+ yields.
        /// X1(T, β) = k1* STY(Tw)*F1(β)/GHSV, for CH4
        /// X2+(T, β) = k2* STY(Tw)*F2(β)/GHSV, for all the C2+ species
        /// F1(β) =( 0.0972β – 0.0987)/ 0.060708 and F1(β) > 0, 
        /// F2(β) = (0.142β +0.1033)/ 0.33618
        /// </summary>
        /// <param name="ratioH2CO">The mole ratio H2/CO</param>
        /// <returns>The base component converting to C1-Cn's mole fractions.</returns>
        private double[] MoleFracsUsingH2CORatio(double ratioH2CO)
        {
            if (selAlpha <= 0 || selAlpha >= 1)
            {
                throw new InvalidOperationException("The selectivity alpha value is out of range.");
            }

            if (ratioH2CO <= 0)
            {
                throw new ArgumentException("Invalid H2/CO ratio", "ratioH2CO");
            }

            //double ratio = InPort.Fraction[Convert.ToInt32(fracMap[FTPropPackage.INDEX_H2])] /
            //    InPort.Fraction[Convert.ToInt32(fracMap[FTPropPackage.INDEX_CO])];
            //ConversionBase convBase = ratio > 2.06 ? ConversionBase.CO : ConversionBase.H2;
            double[] convCoeffMap2BaseComp = CalcChainGrowth(convBase, selAlpha);

            // Calculate the CO conversion
            double sty = FTPropPackage.GetSTYByTemperature(fixedTemperature - 273.15, FTPropPackage.SpaceTimeYields);
            double yieldCH4 = FTPropPackage.CalcYCH4UsingH2CORatio(k1Coeff, sty, reactorVol, InPort.TotalFlow,
                fixedTemperature, InPort.Pressure, ratioH2CO);
            double yieldC2Plus = FTPropPackage.CalcYC2PlusUsingH2CORatio(k2Coeff, sty, reactorVol, InPort.TotalFlow,
                fixedTemperature, InPort.Pressure, ratioH2CO);
            conversion = yieldCH4 + yieldC2Plus;
            if (conversion > 0.7)
            {
                yieldCH4 *= 0.7 / conversion;
                yieldC2Plus *= 0.7 / conversion;
                conversion = 0.7;
            }

            // Adjust the components yields according to the above results.
            double normalizationFactor = 1;
            double leftTotal = 1 - convCoeffMap2BaseComp[0];
            convCoeffMap2BaseComp[0] = yieldCH4;
            for (int i = 1; i < convCoeffMap2BaseComp.Length; ++i)
            {
                convCoeffMap2BaseComp[i] *= normalizationFactor / leftTotal * yieldC2Plus;
            }

            return convCoeffMap2BaseComp;
        }

        /// <summary>
        /// Calculate the component yields using the chain growth.
        /// </summary>
        /// <param name="convBase">The base conversion component.</param>
        /// <param name="alpha">The chain growth's alpha value.</param>
        /// <returns>The chain array.</returns>
        private double[] CalcChainGrowth(ConversionBase convBase, double alpha)
        {
            // Calculate the components distribution using the chain growth.
            const int carbonCount = 62;  // C1 - C62
            double[] temps = new double[carbonCount];
            double totalFrac = 0;
            for (int i = 0; i < carbonCount - 2; i++)
            {
                temps[i] = (1 - alpha) * Math.Pow(alpha, (double)i);
                totalFrac += temps[i];
            }
            List<double> fracs = new List<double>();

            // C1 - C30
            for (int i = 0; i < 30; i++)
            {
                fracs.Add(temps[i]);
            }

            // "C31-C32","C33-C34","C35-C36","C37-C38","C39-C40","C41-C42","C43-C44",
            for (int i = 30; i < 44; i += 2)
            {
                fracs.Add(temps[i] + temps[i + 1]);
            }
            for (int i = 44; i < carbonCount - 1; i += 3)
            {
                fracs.Add(temps[i] + temps[i + 1] + temps[i + 2]);
            }
            fracs.Add(1 - totalFrac);
            fracs.Add(0.0);     // CO2

            // Converted to base component
            double[] convCoeffMapBaseComp = fracs.ToArray();
            double total = 0.0;
            for (int i = 0; i < convCoeffMapBaseComp.Length; ++i)
            {
                if (convBase == ConversionBase.CO)
                {
                    convCoeffMapBaseComp[i] = 
                        Math.Abs(convCoeffMapBaseComp[i] * FTPropPackage.StoiCoefCO[i] / FTPropPackage.StoiCoefCn[i]);
                }
                else if (convBase == ConversionBase.H2)
                {
                    convCoeffMapBaseComp[i] = 
                        Math.Abs(convCoeffMapBaseComp[i] * FTPropPackage.StoiCoefH2[i] / FTPropPackage.StoiCoefCn[i]);
                }
                else
                {
                    throw new InvalidOperationException("Unsupported base conversion component.");

                }
                total += convCoeffMapBaseComp[i];
            }
            for (int i = 0; i < convCoeffMapBaseComp.Length; ++i)
            {
                convCoeffMapBaseComp[i] /= total;
            }

            return convCoeffMapBaseComp;
        }

        #endregion
    }
}
